package hyl.core.io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import hyl.core.AIni;
import hyl.core.MyCharset;
import hyl.core.MyDate;
import hyl.core.MyFun;
import hyl.core.run.IDoEach1;

/**
 * utf8 BOM头是window独有的,在文件最开头加入了16进制的0xEF 0xBB 0xBF这三位,用来识别utf8格式<br>
 * 本工具类,默认情况不考虑bom格式输出, <br>
 * 如果要输出utf8的bom格式 ,需要在写入前调用专门的函数setUtf8BomHead()<br>
 * 
 * utf8的bom格式 文件 读取时,java 会自动跳过 三个字节 ,不用自己过滤;<br>
 * 
 * @author 阿友 3798955@qq.com
 *
 */
public class MyFile {
	private static final Logger log = LoggerFactory.getLogger(MyFile.class);
	private static final int CACHE_SIZE = 256;
	private static final int BYTES_SIZE = 2048;
	// 0:其他文件, 1:数据文件,2:缓存文件 a:身份证图片 ,b:银行卡图片 ,c:人像图片,d:产品图,e:网页图片
	public static final char scope_file_未知 = '0';
	public static final char scope_file_缓存 = '1';
	public static final char scope_file_聊天 = '2';
	public static final char scope_file_数据 = '3';
	public static final char scope_img_身份证 = 'a';
	public static final char scope_img_银行卡 = 'b';
	public static final char scope_img_人像 = 'c';
	public static final char scope_img_产品 = 'd';
	public static final char scope_img_文档 = 'e';
	public static final char scope_img_聊天 = 'f';
	public static final char scope_img_其他 = 'i';

	/**
	 * 从项目根路径开始查找
	 * 
	 * @param 查找目录或文件
	 * @return 返回null 表示没有找到
	 */
	public static File find(String 查找目录或文件) {
		return find(new File(MyPath.getPathOfPro()), 查找目录或文件);
	}

	/**
	 * 找到一个匹配文件就返回 ,速度快
	 * 
	 * @param 从当前目录
	 * @param 查找目录或文件
	 * @return 返回null 表示没有找到
	 * 
	 *         file.getPath().replace('\\', '/'); 可以得到路径
	 */
	public static File find(File 从当前目录, String 查找目录或文件) {
		// File f=new File(从当前目录);

		if (!从当前目录.exists())
			return null;
		if (从当前目录.isDirectory()) {

			File[] files = 从当前目录.listFiles();
			if (files == null) {
				return null;
			}
			for (File file : files) {
				String name = file.getName();
				// if (file.getPath().endsWith(suffix))
				if (name.equals(查找目录或文件)) {
					return file;

				} else {// 如果不等 并且是目录 那么继续深入
					if (file.isDirectory()) {
						File s = find(file, 查找目录或文件);
						if (s != null)
							return s;
					}
					// 否则 继续找下一个 文件
				}
			}
		}
		return null;
	}

	/**
	 * 找到所有匹配的文件,速度慢
	 * 
	 * @param 从当前目录
	 * @param 查找目录或文件
	 * @param 找到的文件容器
	 */
	public static void find(File 从当前目录, String 查找目录或文件, List<File> 找到的文件容器) {
		// File f=new File(从当前目录);

		if (!从当前目录.exists())
			return;
		if (从当前目录.isDirectory()) {
			File[] files = 从当前目录.listFiles();
			if (files == null) {
				return;
			}
			for (File file : files) {
				String name = file.getName();
				// if (file.getPath().endsWith(suffix))
				if (name.equals(查找目录或文件)) {
					找到的文件容器.add(file);

				} else {// 如果不等 并且是目录 那么继续深入
					if (file.isDirectory()) {
						find(file, 查找目录或文件, 找到的文件容器);
					}
					// 否则 继续找下一个 文件
				}
			}
		}

	}

	/**
	 * 查找路径匹配的文件
	 * 
	 * @param 从当前目录
	 * @param 这里不是多个文件 按给定路径查找目录或文件 new String[dir][file] = "dir/file" = 路径
	 * @return
	 */
	static File find(File 从当前目录, String[] 查找目录或文件) {
		// File f=new File(从当前目录);

		if (!从当前目录.exists())
			return null;
		if (从当前目录.isDirectory()) {

			File[] files = 从当前目录.listFiles();
			if (files == null) {
				return null;
			}
			for (File file : files) {
				String name = file.getName();
				// if (file.getPath().endsWith(suffix))
				if (name.equals(查找目录或文件[0])) {
					if (查找目录或文件.length > 1) { // 如果找到路径继续往下找
						String paths[] = Arrays.copyOfRange(查找目录或文件, 1, 查找目录或文件.length);
						return find(file, paths);
					}

				} else {// 如果不等 并且是目录 那么继续深入
					if (file.isDirectory()) {
						File s = MyFile.find(file, 查找目录或文件);
						if (s != null)
							return s;
					}
					// 否则 继续找下一个 文件
				}
			}
		}
		return null;
	}

	public static File openDir(String path) {
		if (MyFun.isEmpty(path))
			return null;
		File file = new File(path);
		if (!file.exists()) {
			file.mkdirs();
		}
		return file;
	}

	public static File openDir(File dir) {
		if (!dir.exists()) {
			dir.mkdirs();
		}
		return dir;
	}

	public static File openFile(String path) {
		if (MyFun.isEmpty(path))
			return null;
		// System.out.println(path);
		File file = new File(path);
		return openFile(file);
	}

	public static File openFile(File file) {
		if (file.exists())
			return file;
		File fileParent = file.getParentFile();
		if (!fileParent.exists()) {
			fileParent.mkdirs();
		}
		try {
			file.createNewFile();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return file;
	}

	public static URL getUrl(String file) throws IOException {
		if (MyFun.isEmpty(file))
			throw new IOException("缺少包名");
		File f = new File(file);
		if (f.exists() == false)
			throw new IOException("包文件不存在");
		URL url1 = f.toURI().toURL();
		return url1;
	}

	public static boolean exist(String path) {
		if (MyFun.isEmpty(path))
			return false;
		File file = new File(path);
		return file.exists();
	}

	public static boolean deleteDir(String path) {
		File file = new File(path);
		return deleteDir(file);
	}

	public static boolean deleteDir(File file) {
		if (!file.exists()) {// 判断是否待删除目录是否存在
			log.error(file.getPath() + "目录不存在!");
			return false;
		}
		if (file.isDirectory()) {
			for (File file2 : file.listFiles()) {
				deleteDir(file2);
			}
			file.delete();
		} else {
			file.delete();
		}
		return true;
	}

	public static boolean deleteFile(String path) {
		File file = new File(path);
		return deleteFile(file);
	}

	public static boolean deleteFile(File file) {
		if (file.exists()) {// 判断是否待删除目录是否存在
			file.delete();
			return true;
		} else
			return false;
	}

	public static FileOutputStream getFileOutputStream(String allpath) {
		try {
			return new FileOutputStream(new File(allpath));
		} catch (FileNotFoundException e) {

			return null;
		}
	}

	public static FileInputStream getFileInputStream(String allpath) {
		try {

			return new FileInputStream(new File(allpath));
		} catch (FileNotFoundException e) {

			return null;
		}
	}

	/**
	 * 只适合查找资源文件包含配置文件<br>
	 * 先从编译路径取 <br>
	 * 再从加载环境的包路径下找到文件 <br>
	 * 再从项目根路径下找<br>
	 * 最后中绝对路径下找<br>
	 * 返回空 表示无法找到
	 */
	public static InputStream findInputstream(String path) {
		if (MyFun.isEmpty(path)) {
			return null;
		}
		InputStream inputStream = null;
		try {

			ClassLoader clazz = MyFile.class.getClassLoader();
			// 先从编译路径的根目录下找
			inputStream = MyPath.getInputStreamFromClassLoadFile(clazz, path);
			// MyPath.test(MyFile.class);
			// 再从资源根路径下找<br>

			if (inputStream == null) {

				String s = MyPath.getPathOfClasses(MyFile.class);
				if (!MyFun.isEmpty(s)) {
					File f = find(new File(s), path);
					if (MyFun.isExist(f))
						inputStream = new FileInputStream(f);
				}
			}
			// 再从项目根路径下找<br>
			if (inputStream == null) {
				File f = find(path);
				// System.out.println(f.getPath());
				if (MyFun.isExist(f))
					inputStream = new FileInputStream(f);
			}
			if (inputStream == null)
				// 假设是绝对路径,从绝对路径开始找
				inputStream = getFileInputStream(path);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return inputStream;
	}

	/**
	 * 只适合查找资源文件包含配置文件<br>
	 * 返回 InputStreamReader 用于字符流
	 */
	public static InputStreamReader findInputstreamRead(String path, Charset encode) {
		if (MyFun.isEmpty(path)) {
			return null;
		}
		InputStream is = MyFile.findInputstream(path);
		if (is == null)
			return null;
		return new InputStreamReader(is, encode);

	}

	/**
	 * 根据文件指针，读取文件 注意文件不能为bom开头的utf-8 如果是请转成无bom 的utf-8
	 */
	public static InputStream getInputstream(File file) {
		if (file == null) {
			return null;
		}
		InputStream inputStream = null;
		try {
			inputStream = new FileInputStream(file);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		return inputStream;
	}

	/**
	 * 读取字符流文件 注意文件不能为bom开头的utf-8 如果是请转成无bom 的utf-8
	 */
	public static InputStreamReader getInputstreamRead(String path, Charset encode) {
		if (MyFun.isEmpty(path)) {
			return null;
		}
		return getInputstreamRead(new File(path), encode);
	}

	/**
	 * 读取字符流文件 注意文件不能为bom开头的utf-8 如果是请转成无bom 的utf-8
	 */
	public static InputStreamReader getInputstreamRead(File file, Charset encode) {
		if (file == null) {
			return null;
		}
		Charset filecode = (encode == null) ? MyCharset._UTF8 : encode;
		InputStream inputStream = MyFile.getInputstream(file);
		if (inputStream == null)
			return null;
		InputStreamReader isr = new InputStreamReader(inputStream, filecode);
		return isr;
	}

	/**
	 * 默认覆盖写入 也就是刷新
	 * 
	 * @param path
	 * @return
	 */
	public static OutputStream getOutputstream(String path) {
		if (MyFun.isEmpty(path)) {
			return null;
		}
		return getOutputstream(new File(path), false);
	}

	public static OutputStream getOutputstream(File file, boolean 是否追加) {
		if (file == null) {
			return null;
		}
		OutputStream stream = null;
		try {
			stream = new FileOutputStream(file, 是否追加);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

		return stream;
	}

	public static OutputStreamWriter getOutputStreamWriter(File file, boolean 是否追加, String 编码) {
		if (file == null) {
			return null;
		}
		OutputStream stream = getOutputstream(file, 是否追加);
		try {
			OutputStreamWriter isr = new OutputStreamWriter(stream, 编码);
			return isr;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

	}

	/**
	 * 字节流，允许多次写入，写入后不关闭
	 * 
	 * @param output 输出流
	 * @param input  输入流
	 */
	public static void in2out(OutputStream output, InputStream input) {
		try {
			byte[] buf = new byte[BYTES_SIZE];
			int bytesRead;
			while ((bytesRead = input.read(buf)) != -1) {
				output.write(buf, 0, bytesRead);
			}
			output.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void in2out(OutputStream output, InputStream input, int 缓冲大小, boolean 结束是否关闭) {
		try {
			byte[] buf = new byte[缓冲大小];
			int bytesRead;
			while ((bytesRead = input.read(buf)) != -1) {
				output.write(buf, 0, bytesRead);
			}
			output.flush();
			if (结束是否关闭) {
				output.close();
				input.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 字符流
	 * 
	 * @param output
	 * @param input
	 * @param cset
	 */
	public static void in2out(InputStream input, OutputStream output, Charset incharset, Charset outcharset) {
		if (null == incharset)
			incharset = AIni.charset;
		if (null == outcharset)
			outcharset = AIni.charset;
		BufferedReader bReader;
		try {
			// 以xx编码读取
			bReader = new BufferedReader(new InputStreamReader(input, incharset));
			// 以xx编码写出
			BufferedWriter bWriter = new BufferedWriter(new OutputStreamWriter(output, outcharset));
			String line = null;
			while ((line = bReader.readLine()) != null) {
				bWriter.write(line);
				bWriter.newLine();
			}
			bWriter.flush();
			bReader.close();
			bWriter.close();
			output.close();
			input.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void setUtf8BomHead(OutputStream os) {
		try {
			os.write(new byte[] { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF });
		} catch (IOException e) {
			System.out.println("添加bom头失败");
		}
	}

	/**
	 * 只适合一次性插入字节串到文件
	 * 
	 * @param filepath 插入文件的路径
	 * @param bytes    插入数据
	 * @return 是否插入成功
	 */
	public static boolean appendBytes(String filepath, byte[] bytes) {
		File f = new File(filepath);
		return appendBytes(f, bytes);
	}

	/**
	 * 输出流或文件不关闭<br>
	 * 需要手动关闭
	 * 
	 * @param 指定文件
	 * @param bytes 插入数据
	 * @return 是否插入成功
	 */
	private static boolean appendBytes(File 输出文件, byte[] bytes) {
		if (输出文件.getParent() != null)
			openDir(输出文件.getParent());
		FileOutputStream os;
		try {
			os = new FileOutputStream(输出文件, true);
			appendBytes(os, bytes);
			return true;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 
	 * @param os    输出流
	 * @param bytes 插入字节
	 * @return
	 */
	public static boolean appendBytes(OutputStream os, byte[] bytes) {
		try (BufferedOutputStream bos = new BufferedOutputStream(os);) {
			bos.write(bytes);
			bos.flush();
			return true;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * 
	 * @param file   写入文件，如果不存在创建
	 * @param append true 插入。false 覆盖
	 * @param bytes  写入字节
	 * @return
	 */
	public static boolean writeBytes(File file, boolean append, byte[] bytes) {

		openFile(file);
		FileOutputStream os;
		try {
			os = new FileOutputStream(file, append);
			appendBytes(os, bytes);
			os.close();
			return true;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 只适合一次性覆盖字节串到文件
	 * 
	 * @param filepath 写入文件路径
	 * @param bytes    写入字节
	 * @return
	 */
	public static File writeBytes(String filepath, byte[] bytes) {
		File f = new File(filepath);
		if (writeBytes(f, false, bytes))
			return f;
		else
			return null;
	}

	/**
	 * 只适合一次性覆盖字节串到文件
	 * 
	 * @param file  写入文件
	 * @param bytes 写入字节
	 * @return
	 */
	public static boolean writeBytes(File file, byte[] bytes) {
		return writeBytes(file, false, bytes);
	}

	/**
	 * <p>
	 * Description: 批量写入数据
	 * </p>
	 * 
	 * @param os  写入到输出流
	 * @param itt 写入数据是集合
	 * @return 是否写入成功
	 * @throws IOException
	 */
	public static boolean writeBatchBytes(OutputStream os, Iterator<byte[]> itt) throws IOException {
		try (BufferedOutputStream bos = new BufferedOutputStream(os);) {
			while (itt.hasNext()) {
				bos.write(itt.next());
			}
			bos.flush();
			bos.close();
			return true;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * 
	 * 只适合一次性覆盖写入文本 如果分批写入 可以参考源码<br>
	 * 
	 * 
	 * 把内容写入文件
	 * 
	 * @param file    写入文件
	 * @param content 写入字符串内容
	 * @param charset 字符串的字符集
	 * @return
	 */
	public static boolean writeFile(File file, String content, Charset charset) {
		if (charset == null)
			charset = MyCharset._UTF8;
		FileOutputStream fos;
		try {
			fos = new FileOutputStream(file, false);
			OutputStreamWriter osw = new OutputStreamWriter(fos, charset);
			osw.write(content);
			osw.flush();
			osw.close();
			return true;
		} catch (IOException e) {
			return false;
		}
	}

	/**
	 * 8k以内的数据写入快速
	 */
	public static void writeStream(OutputStreamWriter 输出流, String 输出内容) {
		try {
			输出流.write(输出内容);
			输出流.flush();

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void writeStream(OutputStream 输出流, byte[] 输出内容) {
		try {
			输出流.write(输出内容);
			输出流.flush();

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void close(OutputStream os) {
		try {
			os.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void close(InputStream is) {
		try {
			is.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 保存文件，保存后关闭
	 * 
	 * @param nameType    文件名类型 1:uuid,0或者空:自定义
	 * @param is          源文件输入流
	 * @param tagfilepath 目标文件路径
	 * @param filename    目标文件名
	 * @return 目标文件 如果为null 表示保存失败
	 * @Description: 把文件保存到目标路径下
	 * @author zoudy qq:37798955
	 * @date 2018年2月26日 下午5:50:19
	 */
	public static File saveFile(InputStream is, String tagfilepath, String filename) {
		// 获取文件名
		return saveFile(is, tagfilepath + File.separator + filename);
	}

	public static File saveFile(InputStream is, String outfileallpath) {
		// 获取文件名
		File outfile = openFile(outfileallpath);
		saveFile(is, outfile);
		return outfile;
	}

	public static void saveFile(InputStream is, File outfile) {
		// 获取文件名
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream(outfile);
			in2out(fos, is);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				fos.close();
				is.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	/**
	 * 
	 * @param nameType    文件类型参数 0 不改名称， 1 用uuid重新命名 ，2用时间重新命名（不适高并发）， 3
	 *                    用时间戳命名（不适高并发）
	 * @param is          输入流
	 * @param tagfilepath 目标文件路径
	 * @param filename    目标文件名
	 * @return 目标文件
	 */
	public static File saveFile(Integer nameType, InputStream is, String tagfilepath, String filename) {
		tagfilepath = tagfilepath + "/" + nameType;
		return saveFile(is, tagfilepath, filename);
	}

	/**
	 * 用通道方式保存文件，保存后关闭
	 * 
	 * @param inputChannel  输入通道
	 * @param outputChannel 输出通道
	 */
	public static void save2FileChannel(FileChannel inputChannel, FileChannel outputChannel) {
		try {
			outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
			outputChannel.close();
			inputChannel.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 复制文件
	 * 
	 * @param src  源文件
	 * @param dest 目标文件
	 * @return 是否成功
	 */
	public static boolean copyFile(File src, File dest) {
		return copyFile(src, dest, null);
	}

	/**
	 * 复制文件，复制结束关闭
	 * 
	 * @param src   源文件
	 * @param dest  目标文件
	 * @param start 开始位置
	 * @return 是否成功
	 */
	public static boolean copyFile(File src, File dest, Long start) {
		try {
			RandomAccessFile raf1 = new RandomAccessFile(src, "r");
			RandomAccessFile raf2 = new RandomAccessFile(dest, "rw");
			if (start != null)
				raf1.seek(start); // 设置读指针的位置
			byte[] b = new byte[BYTES_SIZE];
			int len = 0;
			while ((len = raf1.read(b)) != -1) {
				raf2.write(b, 0, len);
			}
			raf1.close();
			raf2.close();
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * 复制文件，复制结束关闭流
	 * 
	 * @param input  输入流
	 * @param output 输出流
	 */
	public static void copyFile(InputStream input, OutputStream output) {
		in2out(output, input);
		try {
			output.flush();
			output.close();

			input.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 复制文件夹
	 * 
	 * @param sourceDir 源目录
	 * @param targetDir 目标目录
	 * @throws IOException
	 */
	public static void copyDirectiory(String sourceDir, String targetDir) throws IOException {
		// 新建目标目录
		(new File(targetDir)).mkdirs();
		// 获取源文件夹当前下的文件或目录
		File[] file = (new File(sourceDir)).listFiles();
		for (int i = 0; i < file.length; i++) {
			if (file[i].isFile()) {
				// 源文件
				File sourceFile = file[i];
				// 目标文件
				File targetFile = new File(new File(targetDir).getAbsolutePath() + File.separator + file[i].getName());
				copyFile(sourceFile, targetFile);
			}
			if (file[i].isDirectory()) {
				// 准备复制的源文件夹
				String dir1 = sourceDir + "/" + file[i].getName();
				// 准备复制的目标文件夹
				String dir2 = targetDir + "/" + file[i].getName();
				copyDirectiory(dir1, dir2);
			}
		}
	}

	public static String read(InputStream is) {
		return read(is, MyCharset._UTF8);
	}

	public static String read(InputStream is, Charset charset) {
		if (charset == null)
			charset = MyCharset._UTF8;

		StringBuilder sb = new StringBuilder();
		BufferedReader br = null;
		String s = null;
		try {

			InputStreamReader ir = new InputStreamReader(is);
			br = new BufferedReader(ir);
			String line = null;
			while (null != (line = br.readLine())) {
				// System.out.println(line);
				sb.append(line).append("\r\n");
			}

			s = sb.toString();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (br != null)
					br.close();
			} catch (IOException e) {
			}
		}

		return s;
	}

	/**
	 * <p>
	 * 文件转换为byte数组
	 * </p>
	 * 
	 * @param filePath 文件路径
	 * @param start    开始位置
	 * @param end      结束位置
	 * @return 读取的字节
	 * @throws Exception
	 */
	public static byte[] readBytes(String filePath, long start, long end) throws Exception {
		return readBytes(new File(filePath), start, end);
	}

	public static byte[] readBytes(File file, long start, long end) throws Exception {
		byte[] data = new byte[0];
		if (file.exists()) {
			FileInputStream in = new FileInputStream(file);
			data = readBytes(in, start, end);
		}
		return data;
	}

	/**
	 * 读取某段数据
	 * 
	 * @param in    输入流
	 * @param start 开始位置
	 * @param end   结束位置
	 * @return 读取的数据
	 * @throws Exception
	 */
	public static byte[] readBytes(InputStream in, Long start, Long end) {
		// FileInputStream in = new FileInputStream(file);
		ByteArrayOutputStream out = new ByteArrayOutputStream(BYTES_SIZE);
		byte[] cache = new byte[CACHE_SIZE];
		int nRead = 0;
		byte[] result = null;
		try {
			while ((nRead = in.read(cache)) != -1) {
				if (start + nRead > end)
					out.write(cache, 0, (int) (end - start));
				else
					out.write(cache, 0, nRead);
				out.flush();
				start += nRead;
			}
			result = out.toByteArray();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				out.close();
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		return result;
	}

	/**
	 * 读取所有数据
	 * 
	 * @param filePath 读取文件的路径
	 * @return 读取的数据
	 * @throws Exception
	 */
	public static byte[] readAllBytes(String filePath) {
		File file = new File(filePath);
		return readAllBytes(file);
	}

	public static byte[] readAllBytes(File file) {
		byte[] data = new byte[0];
		if (file.exists()) {
			FileInputStream in;
			try {
				in = new FileInputStream(file);
				data = readAllBytes(in);
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
		return data;
	}

	/**
	 * 读取流数据
	 * 
	 * @param in 输入流
	 * @return 读取的数据
	 * @throws Exception
	 */
	public static byte[] readAllBytes(InputStream in) {
		// FileInputStream in = new FileInputStream(file);

		ByteArrayOutputStream out = new ByteArrayOutputStream(CACHE_SIZE * 4);
		byte[] cache = new byte[CACHE_SIZE];
		int nRead = 0;
		try {
			while ((nRead = in.read(cache)) != -1) {
				out.write(cache, 0, nRead);
				out.flush();
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				out.close();
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return out.toByteArray();
	}

	/**
	 * 通过异步方式读取文件，并处理
	 * 
	 * @param filePath 文件路径
	 * @param each     读取成功后的异步处理方法
	 * @throws Exception
	 */
	public static void readBytesByEach(String filePath, IDoEach1<byte[]> each) {
		File file = new File(filePath);
		if (file.exists()) {
			FileInputStream in;
			try {
				in = new FileInputStream(file);
				readBytesByEach(in, each);
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}

		}
	}

	/**
	 * 通过异步方式读取文件，并处理
	 * 
	 * @param in   输入流
	 * @param each 读取成功后的异步处理方法
	 * @throws Exception
	 */
	public static void readBytesByEach(InputStream in, IDoEach1<byte[]> each) {
		BufferedInputStream bis = new BufferedInputStream(in);
		// 采用通道方式读取,可以并行处理
		// RandomAccessFile raf = new RandomAccessFile(file, "r");
		// raf.seek(start);
		// ByteArrayOutputStream out = new ByteArrayOutputStream();
		byte[] cache = new byte[BYTES_SIZE];
		int nRead = 0;
		Long index = 0L;
		try {
			while ((nRead = bis.read(cache)) != -1) {
				if (nRead == BYTES_SIZE) {
					each.isOver(index, cache);
				} else {
					byte[] dest = new byte[nRead];
					System.arraycopy(cache, 0, dest, 0, nRead);
					each.isOver(index, dest);
				}
				index += nRead;
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				bis.close();
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * <p>
	 * 读取文件中的文本
	 * </p>
	 * 
	 * @param 文件流
	 * @param charset 编码
	 * @return 读取的字符串
	 * @throws IOException
	 */
	public static String readAllByString(InputStream file, Charset charset) {
		if (file == null)
			return null;
		StringBuilder sb = new StringBuilder();
		InputStreamReader isr;
		BufferedReader br;
		if (null == charset)
			charset = MyCharset._UTF8;
		isr = new InputStreamReader(file, charset);
		br = new BufferedReader(isr);
		String line = null;
		boolean len = false;
		try {
			while ((line = br.readLine()) != null) {
				if (len) // 处理换行符的问题
				{
					sb.append("\r\n" + line);
				} else {
					sb.append(line);
					len = true;
				}
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {

			try {
				br.close();
				isr.close();
			} catch (IOException e) {

				e.printStackTrace();
			}
		}

		return sb.toString();
	}

	/**
	 * 读取文件
	 * 
	 * @param file    输入流
	 * @param charset 解码字符集
	 * @return 返回读取的字符串数据
	 * @throws IOException
	 */
	public static String readAllByChars(InputStream file, Charset charset) {
		if (file == null)
			return null;
		StringBuilder sb = new StringBuilder();
		InputStreamReader isr;
		if (null == charset)
			charset = MyCharset._UTF8;
		isr = new InputStreamReader(file, charset);
		int CHARSIZE = 200;
		int nRead = 0;
		char[] cache = new char[CHARSIZE];
		try {
			while ((nRead = isr.read(cache)) != -1) {
				sb.append(cache, 0, nRead);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				isr.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return sb.toString();
	}

	/**
	 * 异步方式读取输入流，并对每一行进行处理
	 * 
	 * @param file        输入流
	 * @param charsetName 解码字符集
	 * @param each        处理方法
	 * @throws Exception
	 */
	public static void readLineByEach(InputStream file, String charsetName, IDoEach1<String> each) throws Exception {
		if (each == null)
			return;
		if (file == null)
			return;
		InputStreamReader insreader = new InputStreamReader(file, charsetName);
		BufferedReader bufReader = new BufferedReader(insreader);
		String s;
		long i = 0;
		while ((s = bufReader.readLine()) != null) {
			each.isOver(i, s);
			i++;
		}
		bufReader.close();
		insreader.close();
	}

	/**
	 * 异步方式读取文件，并对每一行进行处理
	 * 
	 * @param filePath    文件路径
	 * @param charsetName 解码字符集
	 * @param each        处理方法
	 * @throws Exception
	 */
	public static void readLineByEach(String filePath, String charsetName, IDoEach1<String> each) throws Exception {
		if (each == null)
			return;
		File file = new File(filePath);
		if (file.exists()) {
			readLineByEach(new FileInputStream(file), charsetName, each);
		}
	}

	/**
	 * 先根据过滤器 ，分析需要删除的行，最后删除文件中的指定行
	 * 
	 * @param file        文件
	 * @param charsetName 解码字符集
	 * @param each        过滤器
	 */
	public static void deleteLines(File file, String charsetName, IDoEach1<String> each) {
		LinkedList<Long> que = new LinkedList<>();
		RandomAccessFile raf = null;
		String line;
		Long i = 0L;
		try {
			raf = new RandomAccessFile(file, "rw");
			Long writePosition = raf.getFilePointer();
			byte[] buff = new byte[BYTES_SIZE];
			Long position = writePosition;
			Long readPosition = writePosition;
			boolean delflag = false;
			while ((line = raf.readLine()) != null) {
				position = raf.getFilePointer();
				if (delflag) {
					que.addLast(position);// read pos
					delflag = false;
				}
				delflag = each.isOver(i, line);
				if (delflag) {
					que.addLast(position);// write pos
				}
				i++;
			}
			int n = 0;
			while ((readPosition = que.pollLast()) != null) {
				raf.seek(readPosition);
				writePosition = que.pollLast();
				while (-1 != (n = raf.read(buff))) {
					raf.seek(writePosition);
					raf.write(buff, 0, n);
					readPosition += n;
					writePosition += n;
					raf.seek(readPosition);
				}
				raf.setLength(writePosition);
			}
		} catch (IOException e) {
		} finally {
			try {
				if (raf != null) {
					raf.close();
				}
			} catch (IOException e) {
			}
		}
	}

	/**
	 * 获取文件行行数
	 * 
	 * @param file 文件
	 * @return
	 */
	public static int getLineNum(File file) {
		long fileLength = file.length();
		try {
			LineNumberReader lineNumberReader = new LineNumberReader(new FileReader(file));
			lineNumberReader.skip(fileLength);
			int lines = lineNumberReader.getLineNumber();
			lineNumberReader.close();
			return lines;
		} catch (IOException e) {
			return 0;
		}
	}

	/**
	 * 压缩成ZIP 方法1
	 * 
	 * @param srcDir           压缩文件夹路径
	 * @param out              压缩文件输出流
	 * 
	 * @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构;
	 *                         false:所有文件跑到压缩包根目录下(注意：不保留目录结构可能会出现同名文件,会压缩失败)
	 * @throws RuntimeException 压缩失败会抛出运行时异常
	 */
	public static void doZipDir(String srcDir, OutputStream out) throws RuntimeException {
		long start = System.currentTimeMillis();
		ZipOutputStream zos = null;
		try {
			zos = new ZipOutputStream(out);
			File sourceFile = new File(srcDir);
			compress(sourceFile, null, zos);
			long end = System.currentTimeMillis();
			log.info("压缩完成，耗时：" + (end - start) + " ms");
		} catch (Exception e) {
			throw new RuntimeException("MyFile.doZipDir异常：", e);
		} finally {
			if (zos != null) {
				try {
					zos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * * 压缩成ZIP 方法2 *
	 * 
	 * @param srcFiles 需要压缩的文件列表*
	 * @param out      压缩文件输出流
	 * @throws RuntimeException 压缩失败会抛出运行时异常
	 */
	public static void doZipFiles(List<File> srcFiles, OutputStream out) throws RuntimeException {
		long start = System.currentTimeMillis();
		ZipOutputStream zos = null;
		try {
			zos = new ZipOutputStream(out);
			for (File srcFile : srcFiles) {
				compress(srcFile, null, zos);
			}
			long end = System.currentTimeMillis();
			log.info("压缩完成，耗时：" + (end - start) + " ms");
		} catch (Exception e) {
			throw new RuntimeException("MyFile.doZipFiles异常：", e);
		} finally {
			if (zos != null) {
				try {
					zos.flush();
					zos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * * 压缩成ZIP 方法3 *
	 * 
	 * @param srcFiles 需要压缩的文件列表*
	 * @param out      压缩文件输出流
	 * @throws RuntimeException 压缩失败会抛出运行时异常
	 */
	public static void doZipFile(File srcFile, OutputStream out) throws RuntimeException {
		long start = System.currentTimeMillis();
		ZipOutputStream zos = null;
		try {
			zos = new ZipOutputStream(out);
			compress(srcFile, null, zos);
			long end = System.currentTimeMillis();
			log.info("压缩完成，耗时：" + (end - start) + " ms");
		} catch (Exception e) {
			throw new RuntimeException("MyFile.doZipFile异常", e);
		} finally {
			if (zos != null) {
				try {
					zos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 递归压缩方法 把一个一个文件压缩
	 * 
	 * @param sourceFile       源文件
	 * @param zos              zip输出流 目标文件
	 * @param dir              指定在压缩文件内部的文件名称
	 * @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构; *
	 *                         false:所有文件跑到压缩包根目录下(注意：不保留目录结构可能会出现同名文件,会压缩失败)
	 * @throws Exception
	 * 
	 */
	private static void compress(File sourceFile, String parentdir, ZipOutputStream zos) throws Exception {
		if (sourceFile.isFile()) {
			if (MyFun.isEmpty(parentdir))
				parentdir = sourceFile.getName();
			// 向zip输出流中添加一个zip实体，构造器中name为zip实体的文件的名字
			zos.putNextEntry(new ZipEntry(parentdir));
			FileInputStream fis = new FileInputStream(sourceFile);
			in2out(zos, fis);
			zos.closeEntry();
			fis.close();
		} else {
			if (MyFun.isEmpty(parentdir)) {
				parentdir = "";
			} else {
				parentdir += "/";
			}
			File[] listFiles = sourceFile.listFiles();
			if (listFiles == null || listFiles.length == 0) {
				// 需要保留原来的文件结构时,需要对空文件夹进行处理
				zos.putNextEntry(new ZipEntry(parentdir));
				zos.closeEntry();
			} else {
				for (File file : listFiles) {
					// 注意：file.getName()前面需要带上父文件夹的名字加一斜杠,
					// 不然最后压缩包中就不能保留原来的文件结构,即：所有文件都跑到压缩包根目录下了
					compress(file, parentdir + file.getName(), zos);
				}
			}
			// zos.closeEntry();
		}
	}

	/**
	 * <B>方法名称：</B>获取文件后缀名 包含. <BR>
	 * 
	 * @param fileName * @return 如："jpg"、"txt"、"zip" 等
	 */
	public static String getExtName(String oldname) {
		if (MyFun.isEmpty(oldname))
			return null;
		return oldname.substring(oldname.lastIndexOf("."));
	}

	/**
	 * <B>概要说明：</B>获取文件后缀名 不包含. 如果文件名不包含. 为无后缀名 <BR>
	 * 
	 * @param fileName * @return 如："jpg"、"txt"、"zip" 等
	 */
	public static String getFileExt(String fileName) {
		if (StringUtils.isBlank(fileName) || !fileName.contains(".")) {
			return "";
		} else {
			return fileName.substring(fileName.lastIndexOf(".") + 1);
		}
	}

	public static final int TPUUID = 1;
	public static final int TP时间 = 2;
	public static final int TP时间戳 = 3;
	public static final int TP原名 = 0;

	public static String getNewName(String filename, int type) {
		if (type == TPUUID)
			return MyFun.getUUID() + getExtName(filename);
		else if (type == TP时间) {
			return MyDate.getCurrTime() + getExtName(filename);
		} else if (type == TP时间戳) {
			return MyFun.getNs() + getExtName(filename);
		} else
			return filename;
	}

	public static String getSavePath(String 系统, String 前缀, String 后缀) {
		StringBuilder sb = new StringBuilder();
		if (!MyFun.isEmpty(系统))
			sb.append("/").append(系统);
		if (!MyFun.isEmpty(前缀))
			sb.append("/").append(前缀);
		// sb.append("/").append(MyDate.getNow("yyyy/MM/dd"));
		if (!MyFun.isEmpty(后缀))
			sb.append("/").append(后缀);
		// sb.append("/");
		return sb.toString();

	}

	public static String getDateSavePath(String 系统, String 前缀, String 后缀) {
		StringBuilder sb = new StringBuilder();
		if (!MyFun.isEmpty(系统))
			sb.append("/").append(系统);
		if (!MyFun.isEmpty(前缀))
			sb.append("/").append(前缀);
		sb.append("/").append(MyDate.getNow("yyyy/MM/dd"));
		if (!MyFun.isEmpty(后缀))
			sb.append("/").append(后缀);
		// sb.append("/");
		return sb.toString();
	}

	/**
	 * 解压Zip文件
	 * 
	 * @param zipFileName 需要解压缩的文件的绝对路径
	 * @param parentdir   将文件解压到某个路径
	 * @throws IOException
	 */
	public static void unZip(String zipFileName, String parentdir) throws IOException {
		FileInputStream fis = new FileInputStream(zipFileName);
		ZipInputStream zins = new ZipInputStream(fis);
		ZipEntry entry = null;
		while ((entry = zins.getNextEntry()) != null) {
			FileOutputStream fos = new FileOutputStream(parentdir + "/" + entry.getName());
			in2out(fos, zins);
			fos.close();
			zins.closeEntry();
		}
		zins.close();
		fis.close();
	}

	public static boolean equals(File f1, File f2) throws IOException {
		@SuppressWarnings("resource")
		FileInputStream in1 = new FileInputStream(f1);
		@SuppressWarnings("resource")
		FileInputStream in2 = new FileInputStream(f2);
		byte[] cache1 = new byte[BYTES_SIZE];
		byte[] cache2 = new byte[BYTES_SIZE];
		int nRead1 = 0;
		while ((nRead1 = in1.read(cache1)) != -1) {
			if (in2.read(cache2) == -1)
				return false;
			for (int i = 0; i < nRead1; i++) {
				if (cache1[i] != cache2[i])
					return false;
			}

		}
		in1.close();
		in2.close();
		return true;
	}

}
